﻿/*******************************************************************************

  NLocalizer (C) 2010-2012 Chris Prusik (KAP1 Ltd www.kap1.net)
  The fast, simple solution for localizing .NET applications, by text files.
  Latest version: http://NLocalizer.codeplex.com/

  $Id$

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

*******************************************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using System.IO;
using NLocalizer;

namespace NLocalizerApp
{
    public partial class ImportForm : Form
    {
        public Translation Translation = null;
        public bool FinishReadSource = false;
        public bool FinishWriteSource = false;

        public ImportForm()
        {
            InitializeComponent();
        }

        private void ReadClassName(string fileName, string line, string prefix, ref string className)
        {
            if (className == "")
                className = line.Substring(prefix.Length).Trim();
            else
                throw new Exception(String.Format("Expected one class in file {0}", fileName));
        }

        private void ReadString(string fileName, string[] lines, ref int pos, string prefix, string className, bool isStatic)
        {
            bool csharp = String.Compare(Path.GetExtension(fileName), ".cs", true) == 0;
            string line = lines[pos].Trim();
            line = line.Substring(prefix.Length).Trim();
            if (csharp && line.EndsWith("\";"))
                line = line.Substring(0, line.Length - 2).Trim();
            else if (csharp && line.EndsWith("\" +"))
            {
                line = line.Substring(0, line.Length - "\" +".Length).Trim();
                pos++;
                while (pos < lines.Length && lines[pos].Trim().EndsWith("\";") == false)
                {
                    string addLine = lines[pos].Trim();
                    if (addLine.StartsWith("\"") == false)
                        throw new Exception(String.Format(
                            "Expected start \" in file {0}, pos {0}", fileName, pos));
                    addLine = addLine.Substring(1).Trim();
                    if (addLine.EndsWith("\" +") == false)
                        throw new Exception(String.Format(
                            "Expected end \" + in file {0}, pos {0}", fileName, pos));
                    addLine = addLine.Substring(0, addLine.Length - "\" +".Length).Trim();
                    line += addLine;
                    pos++;
                }
                string lastLine = lines[pos].Trim();
                if (lastLine.StartsWith("\"") == false)
                    throw new Exception(String.Format(
                        "Expected start \" in file {0}, pos {0}", fileName, pos));
                lastLine = lastLine.Substring(1).Trim();
                if (lastLine.EndsWith("\";") == false)
                    throw new Exception(String.Format(
                        "Expected end \"; in file {0}, pos {0}", fileName, pos));
                lastLine = lastLine.Substring(0, lastLine.Length - "\";".Length).Trim();
                line += lastLine;
            }
            else if (csharp == false && line.EndsWith("\""))
                line = line.Substring(0, line.Length - 1).Trim();
            else if (csharp == false && line.EndsWith("\" +"))
            {
                line = line.Substring(0, line.Length - "\" +".Length).Trim();
                pos++;
                while (pos < lines.Length && lines[pos].Trim().EndsWith("\"") == false)
                {
                    string addLine = lines[pos].Trim();
                    if (addLine.StartsWith("\"") == false)
                        throw new Exception(String.Format(
                            "Expected start '\"' in file {0}, pos {0}", fileName, pos));
                    addLine = addLine.Substring(1).Trim();
                    if (addLine.EndsWith("\" +") == false)
                        throw new Exception(String.Format(
                            "Expected end '\" +' in file {0}, pos {0}", fileName, pos));
                    addLine = addLine.Substring(0, addLine.Length - "\" +".Length).Trim();
                    line += addLine;
                    pos++;
                }
                string lastLine = lines[pos].Trim();
                if (lastLine.StartsWith("\"") == false)
                    throw new Exception(String.Format(
                        "Expected start '\"' in file {0}, pos {0}", fileName, pos));
                lastLine = lastLine.Substring(1).Trim();
                if (lastLine.EndsWith("\"") == false)
                    throw new Exception(String.Format(
                        "Expected end '\"' in file {0}, pos {0}", fileName, pos));
                lastLine = lastLine.Substring(0, lastLine.Length - "\"".Length).Trim();
                line += lastLine;
            }
            int equ = line.IndexOf(" = \"");
            if (equ < 0)
                throw new Exception(String.Format(
                    "Expected ' = \"' in file {0}, pos {0}", fileName, pos));
            string property = line.Substring(0, equ);
            if (csharp == false)
                property = property.Replace(" As String", "");
            string message = line.Substring(equ + 4);

            bool isDisallow = line.StartsWith("Name = ");

            isDisallow = (property == "Name") ||
                (property.EndsWith(".Name") && property.StartsWith(message));
            foreach (string disallow in DisallowSuffixesTextBox.Lines)
                if (property.EndsWith(disallow))
                    isDisallow = true;

            if (isDisallow == false && className != "")
            {
                string add = "(" + className + ")" + property + " = " + message;
                if (isStatic)
                    add = "!" + add;
                TranslationListBox.Items.Add(add, true);
            }
        }

        private void ReadSourceFile(string fileName)
        {
            string[] lines = File.ReadAllLines(fileName);
            bool csharp = String.Compare(Path.GetExtension(fileName), ".cs", true) == 0;
            string className = "";
            int pos = 0;
            while (pos < lines.Length)
            {
                string line = lines[pos].Trim();
                if (csharp)
                {
                    if (line.StartsWith("partial class "))
                        ReadClassName(fileName, line, "partial class ", ref className);
                    else if (line.StartsWith("class "))
                        ReadClassName(fileName, line, "class ", ref className);
                    else if (line.StartsWith("public class "))
                        ReadClassName(fileName, line, "public class ", ref className);
                    else if (line.StartsWith("public static class "))
                        ReadClassName(fileName, line, "public static class ", ref className);
                    else if (line.StartsWith("public static string ") && line.IndexOf("= \"") > 0)
                        ReadString(fileName, lines, ref pos, "public static string ", className, true);
                    else if (line.StartsWith("public string ") && line.IndexOf("= \"") > 0)
                        ReadString(fileName, lines, ref pos, "public string ", className, false);
                    else if (line.StartsWith("this.") && line.IndexOf(" = \"") > 0)
                        ReadString(fileName, lines, ref pos, "this.", className, false);
                }
                else
                {
                    if (line.StartsWith("Partial Class "))
                        ReadClassName(fileName, line, "Partial Class ", ref className);
                    else if (line.StartsWith("Class "))
                        ReadClassName(fileName, line, "Class ", ref className);
                    else if (line.StartsWith("Public Class "))
                        ReadClassName(fileName, line, "Public Class ", ref className);
                    else if (line.StartsWith("Public Shared ") && line.IndexOf("= \"") > 0)
                        ReadString(fileName, lines, ref pos, "Public Shared ", className, true);
                    else if (line.StartsWith("Public ") && line.IndexOf("= \"") > 0)
                        ReadString(fileName, lines, ref pos, "Public ", className, false);
                    else if (line.StartsWith("Me.") && line.IndexOf(" = \"") > 0)
                        ReadString(fileName, lines, ref pos, "Me.", className, false);
                }
                pos++;
            }
        }

        private void ReadSourceFilesButton_Click(object sender, EventArgs e)
        {
            try
            {
                TranslationListBox.Items.Clear();
                foreach (string fileName in FilesListBox.CheckedItems)
                    ReadSourceFile(fileName);
                TabControl.SelectTab(TranslationTab);
                FinishReadSource = true;
                UpdateInfoLabel();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void WriteSourceFile(string fileName, List<string> components)
        {
            string[] lines = File.ReadAllLines(fileName);
            bool csharp = String.Compare(Path.GetExtension(fileName), ".cs", true) == 0;
            bool changed = false;
            int pos = 0;
            while (pos < lines.Length)
            {
                string line = lines[pos].Trim();
                string[] startsWith;
                string[] replaceWidth;
                if (csharp)
                {
                    startsWith = new string[] { "private " };
                    replaceWidth = new string[] { "public " };
                }
                else
                {
                    startsWith = new string[] { "Private WithEvents ", "Private " };
                    replaceWidth = new string[] { "Public WithEvents ", "Public " };
                }

                int num;
                for (num = 0; num < startsWith.Length; num++)
                    if (line.StartsWith(startsWith[num]))
                        break;
                if (num < startsWith.Length)
                    foreach (string component in components)
                    {
                        if ((csharp && line.EndsWith(component + ";")) ||
                            (csharp == false && line.IndexOf(" " + component + " ") > 0))
                        {
                            int privatePos = lines[pos].IndexOf(startsWith[num]);
                            lines[pos] = lines[pos].Remove(privatePos, startsWith[num].Length);
                            lines[pos] = lines[pos].Insert(privatePos, replaceWidth[num]);
                            changed = true;
                            break;
                        }
                    }
                pos++;
            }
            if (changed)
                File.WriteAllLines(fileName, lines);
        }

        private void WriteSourceFilesButton_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Correct all private components to public in source files?", "Confirm", 
                MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.OK)
                try
                {
                    List<string> components = new List<string>();
                    foreach (string item in TranslationListBox.CheckedItems)
                    {
                        int bracketPos = item.IndexOf(")");
                        if (bracketPos < 0)
                            throw new Exception(String.Format("Bad definition {0}", item));

                        string line = item.Substring(bracketPos + 1);
                        int pointPos = line.IndexOf(".");
                        if (pointPos > 0)
                        {
                            string component = line.Substring(0, pointPos);
                            if (components.Contains(component) == false)
                                components.Add(component);
                        }
                    }
                    foreach (string fileName in FilesListBox.CheckedItems)
                        WriteSourceFile(fileName, components);
                    FinishWriteSource = true;
                    UpdateInfoLabel();
                    MessageBox.Show("OK, source files modified.\r\nAll translation components are public now.", "Information",
                        MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

        }

        private void OKButton_Click(object sender, EventArgs e)
        {
            if (FinishReadSource == false)
                MessageBox.Show("Please read source files first", "Warning",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
            else
                try
                {
                    List<string> lines = new List<string>();
                    foreach (string item in TranslationListBox.CheckedItems)
                        lines.Add(item);
                    Translation.Read("Neutral", lines.ToArray());
                    foreach (KeyValuePair<string, TranslationProperties> cl in Translation["Neutral"])
                        foreach (KeyValuePair<string, TranslationProperty> prop in cl.Value)
                            if (Translation.Exists(Translation.GetEnglishName(), cl.Key, prop.Key) == false)
                                Translation.SetProperty(Translation.GetEnglishName(), cl.Key, prop.Key, prop.Value.Message, prop.Value.IsStatic);
                    UpdateInfoLabel();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
        }

        private void ImportForm_Shown(object sender, EventArgs e)
        {
            UpdateInfoLabel();
        }

        public void UpdateInfoLabel()
        {
            // public bool FinishReadSource = false;
            // public bool FinishWriteSource = false;
            if (FinishReadSource == false)
                InfoLabel.Text = "Please select source files and click Read button.";
            else if (FinishWriteSource == false)
                InfoLabel.Text = "Please select result properties and click Write button.";
            else
                InfoLabel.Text = "Click OK to get translation.";
        }
    }
}
